package formatter;

import java.io.BufferedWriter;
import java.io.IOException;

import lib.random.ExtendedRandom;

/**
 * Responsible for writing objects into its appropriate topcoder format
 * @author dolphinigle
 */
public class TopCoderFormatter {

  /**
   * Output Stream writer
   */
  private BufferedWriter writer_;

  /**
   * Constructs a new topcoder formatter
   * @param writer output stream capsulator to write to
   */
  public TopCoderFormatter(BufferedWriter writer) {
    writer_ = writer;
  }

  /**
   * Closes the output stream associated with this formatter
   * @throws IOException iff closing fails
   */
  public void close() throws IOException {
    writer_.close();
  }

  /**
   * Format and write an integer
   * @param param integer to be written
   */
  public void writeInt(int param) {
    try {
      writer_.write(String.valueOf(param));
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Format and write a long
   * @param param long to be written
   */
  public void writeLong(long param) {
    try {
      writer_.write(String.valueOf(param));
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Format and write an array of integers
   * @param param array of integers to be written
   */
  public void writeArrayInt(int[] param) {
    try {
      writer_.write("{");
      for (int i = 0; i < param.length; ++i) {
        if (i > 0) writer_.write(",");
        writer_.write(String.valueOf(param[i]));
      }
      writer_.write("}");
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Format and write an array of longs
   * @param param array of longs to be written
   */
  public void writeArrayLong(long[] param) {
    try {
      writer_.write("{");
      for (int i = 0; i < param.length; ++i) {
        if (i > 0) writer_.write(",");
        writer_.write(String.valueOf(param[i]));
      }
      writer_.write("}");
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Format and write a string
   * @param param string to be written
   */
  public void writeString(String param) {
    try {
      writer_.write("\"");
      writer_.write(param);
      writer_.write("\"");
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Format and write an array of string
   * @param param array of string to be written
   */
  public void writeArrayString(String[] param) {
    try {
      writer_.write("{");
      for (int i = 0; i < param.length; ++i) {
        if (i > 0) writer_.write(",");
        writer_.write("\"");
        writer_.write(param[i]);
        writer_.write("\"");
      }
      writer_.write("}");
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Format and write an array of string and put them on different lines
   * @param param array of string to be written
   */
  public void writeNewlineSeparatedArrayString(String[] param) {
    try {
      writer_.write("{");
      for (int i = 0; i < param.length; ++i) {
        if (i > 0) writer_.write(",");
        writer_.write("\"");
        writer_.write(param[i]);
        writer_.write("\"\n");
      }
      writer_.write("}\n");
      writer_.newLine();
    } catch (IOException ios) {
      throw new Error(ios.toString());
    }
  }

  /**
   * Formats and writes an array of long as an array of strings separated
   * by single space
   * <br/><br/>
   * I.e., the concatenation of the result will be the space separated list of
   * integers from the input
   */
  public void writeArrayLongAsArrayString(long[] param,
          int array_length_ub, int element_length_ub) {

    assert array_length_ub > 0;
    assert element_length_ub > 0;

    StringBuilder strbuild = new StringBuilder();
    for (int i = 0; i < param.length; ++i) {
      if (i > 0) {
        strbuild.append(" ");
      }
      strbuild.append(param[i]);
    }

    String str = strbuild.toString();
    int len = str.length();

    int minlength = len / element_length_ub;
    if (len % element_length_ub != 0) ++minlength;

    int maxlength = Math.min(len, array_length_ub);

    ExtendedRandom exr = new ExtendedRandom(100);

    int n = exr.nextInt(minlength, maxlength);

    int content[] = new int[n];
    int remain = len;
    for (int i = 0; i < n; ++i) {
      --remain;
      content[i] = 1;
    }

    while (remain > 0) {
      int pick = exr.nextInt(0, n-1);
      int cntloop = 0;
      while (content[pick] >= element_length_ub) {
        pick += 1;
        pick %= n;
        ++cntloop;
        if (cntloop > 2 * n) {
          throw new Error("Infinite Loop at TOpCoder Formatter");
        }
      }
      content[pick]++;
      --remain;
    }

    String[] arrstr = new String[n];
    int sum = 0;
    for (int i = 0; i < n; ++i) {
      arrstr[i] = str.substring(sum, sum + content[i]);
      sum += content[i];
    }
    this.writeArrayString(arrstr);
  }

}
